<?php
abstract class Generator
{
    /**
     * @var Текс для вывода сведений о правах
     */
    protected $CopyrightText;
    /**
     * @var Путь к логотипу
     */
    protected $LogoFile;
    protected $Logo;
    /**
     * @var Пути к иконкам
     */
    protected $IconsFolder;

    protected $IconsList;

    protected $NewIconFilesList;
    protected $MetaphorsFileList;

    protected $BorderWidth;
    protected $LabelWidth;
    protected $VerticalSpace;
    protected $HorisontalSpace;

    protected $RotateAngle;

    protected $IconSize;

    protected $LabelColor1;
    protected $LabelColor2;

    protected $RowsCount;
    protected $ColsCount;

    protected $MapWidth;
    protected $MapHeight;

    protected $Font;
    protected $FontSize;

    protected $ResultFileName;
    protected $CopyrightFontSize;
    protected $HeaderText;
    protected $HeaderFontSize;
    protected $Version;
    protected $VersionDate;
    protected $OnlyFirst;

    protected $Mode;

    /**
     * @param  $mode Режим генерации
     */
    public function __construct($mode)
    {
        $this->LoadSettings($mode);

        $this->IconsList = $this->GetIcons();

        // Устанавливаем высоту и ширину картинки в иконках

        $this->RowsCount = round($this->GetIconsCount() / $this->ColsCount);

        $this->GetLogo();

        // Устанавливаем высоту и ширину картинки в пикселях
        $this->MapWidth = ($this->IconSize
                + $this->LabelWidth
                + $this->VerticalSpace
        ) * $this->ColsCount
                + 2 * $this->BorderWidth;

        $this->MapHeight = $this->HeaderFontSize
                + 3 * $this->VerticalSpace
                + ($this->IconSize
                        + $this->HorisontalSpace
                ) * $this->RowsCount
                + 2 * $this->VerticalSpace
                + $this->Logo['height']
                + 2 * $this->BorderWidth; // Рамка
    }

    protected function LoadSettings($mode)
    {
        global $CONFIG;
        $this->HeaderText = $CONFIG['Info']['HeaderText'];
        $this->CopyrightText = $CONFIG['Info']['CopyrightText'];
        $this->CopyrightFontSize = $CONFIG['Common']['CopyrightFontSize'];
        $this->LogoFile = $CONFIG['Info']['LogoFile'];
        $this->HeaderFontSize = $CONFIG['Common']['HeaderFontSize'];
        $this->Version = $CONFIG['Info']['Version'];
        $this->VersionDate = $CONFIG['Info']['VersionDate'];

        $this->IconsFolder = $CONFIG['Pathes']['Icons'];
        $this->IconsFolder .= $CONFIG['Mode_' . $mode]['Folder'];

        $this->ResultFileName = $CONFIG['Pathes']['ResultFolder'];
        $this->ResultFileName .= $CONFIG['Mode_' . $mode]['ResultFileName'];

        $this->IconSize = $CONFIG['Mode_' . $mode]['IconSize'];
        $this->LabelWidth = $CONFIG['Mode_' . $mode]['LabelWidth'];
        $this->BorderWidth = $CONFIG['Common']['BorderWidth'];

        $this->VerticalSpace = $CONFIG['Common']['VerticalSpace'];
        $this->HorisontalSpace = $CONFIG['Common']['HorisontalSpace'];
        $this->OnlyFirst = $CONFIG['Mode_' . $mode]['OnlyFirst'];

        $this->LabelColor1 = self::ParseColor($CONFIG['Common']['LabelColor1']);
        $this->LabelColor2 = self::ParseColor($CONFIG['Common']['LabelColor2']);

        $this->Font = $CONFIG['Pathes']['FontFile'];
        $this->FontSize = $CONFIG['Mode_' . $mode]['FontSize'];

        $this->RotateAngle = $CONFIG['Mode_' . $mode]['RotateAngle'];

        $newIconsListFileName = $CONFIG['Pathes']['NewIconsList'];
        $this->NewIconFilesList = self::GetFilesList($newIconsListFileName);

        $metaphorsFileName = $CONFIG['Pathes']['Metaphors'];
        $this->MetaphorsFileList = self::GetFilesList($metaphorsFileName);

        $this->ColsCount = $CONFIG['Common']['ColumnsNumber'];
        $this->Mode = $CONFIG['Common']['Mode'];
    }

    abstract protected function Generate();

    /**
     * Считывает из каталога массив иконок
     * @return array массив иконок
     */
    final protected function GetIcons()
    {
        $files = array();
        $fileList = glob($this->IconsFolder . "*.png");

        foreach ($fileList as $fileName)
        {
            $icon = new Icon($fileName, $this->RotateAngle);
            $basename = basename($fileName);
            if (in_array($basename, $this->NewIconFilesList)) {
                $icon->New = true;
            }
            if (in_array($basename, $this->MetaphorsFileList)) {
                $icon->Metaphor = true;
            }

            // TODO надо как-то упростить, а то непонятно
            if ($this->OnlyFirst) {
                if (strpos($fileName, '_') == 0) {
                    $files[] = $icon;
                }
            }
            else
            {
                $files[] = $icon;
            }
        }
        return $files;
    }

    final public function SaveToFile()
    {
        $dirName = dirname($this->ResultFileName);

        // Если нет папки - пробуем создаем ее
        if (!file_exists($dirName)) {
            if (!mkdir($dirName)) {
                echo "Нет могу записать в каталог " . $dirName . "\n";
                return false;
            }
        }
        $img = $this->Generate();

        imagepng($img, $this->ResultFileName);
        imagedestroy($img);
        return true;
    }

    /**
     * Код взят отсюда: http://www.anyexample.com/programming/php/php_convert_rgb_from_to_html_hex_color.xml
     * @static
     * @param  $color
     * @return array|bool
     */
    final protected static function ParseColor($color)
    {
        {
            if ($color[0] == '#')
                $color = substr($color, 1);

            if (strlen($color) == 6)
                list($r, $g, $b) = array($color[0] . $color[1],
                    $color[2] . $color[3],
                    $color[4] . $color[5]);
            elseif (strlen($color) == 3)
                list($r, $g, $b) = array($color[0] . $color[0],
                    $color[1] . $color[1],
                    $color[2] . $color[2]);
            else
                return false;

            $r = hexdec($r);
            $g = hexdec($g);
            $b = hexdec($b);

            return array('red' => $r, 'green' => $g, 'blue' => $b);
        }
    }

    /**
     * @static
     * @param  $fileName Имя файла со списком
     * @return array список новых иконок (имен файлов)
     */
    final protected static function GetFilesList($fileName)
    {
        $file = file_get_contents($fileName);
        $lines = explode("\r\n", $file);
        return $lines;
    }

    /**
     * Генерирует рамку
     * @param  $width Ширина, минимальная - 14
     * @param  $height Высота, минимальная - 15
     * @return resource Изображение рамки
     */
    final protected static function GenerateFrame($width, $height, $mode)
    {
        global $CONFIG;

        switch ($mode) {
            case 0: // Новые иконки
                $framePath = $CONFIG['Pathes']['FramePartsFolder'];
                break;
            case 1: // Метафоры
                $framePath = $CONFIG['Pathes']['MetaphorsFolder'];
                break;
            default: // Новые иконки
                $framePath = $CONFIG['Pathes']['FramePartsFolder'];
                break;
        }

        if ($width < 14 || $height < 15) {
            return;
        }

        // Формируем пустое изображение
        $result = self::CreateAlphaPng($width, $height);

        // Считываем текстуры
        $parts = array();
        for ($i = 1; $i <= 9; $i += 1)
        {
            $parts[$i] = imagecreatefrompng($framePath . $i . '.png');
        }

        // Добавляем уголки
        // Лево-верх
        imagecopy($result, $parts[1], 0, 0, 0, 0, 4, 7);
        // Право-верх
        imagecopy($result, $parts[3], $width - 4, 0, 0, 0, 4, 7);
        // Лево-низ
        imagecopy($result, $parts[7], 0, $height - 6, 0, 0, 4, 7);
        // Право-низ
        imagecopy($result, $parts[9], $width - 4, $height - 6, 0, 0, 4, 7);

        // Заполняем рамки
        //Верхняя строчка
        imagesettile($result, $parts[2]);
        imagefilledrectangle($result, 4, 0, $width - 5, 6, IMG_COLOR_TILED);

        // Левая часть
        imagesettile($result, $parts[4]);
        imagefilledrectangle($result, 0, 7, 3, $height - 7, IMG_COLOR_TILED);

        // Центр
        imagesettile($result, $parts[5]);
        imagefilledrectangle($result, 4, 7, $width - 5, $height - 7, IMG_COLOR_TILED);

        // Право (изврат из-за тупой реализации черепицы в PHP)
        $p6 = self::CreateAlphaPng(4, $height - 13);

        imagesettile($p6, $parts[6]);
        imagefilledrectangle($p6, 0, 0, 4, $height - 14, IMG_COLOR_TILED);
        imagecopy($result, $p6, $width - 4, 7, 0, 0, 4, $height - 13);

        // Низ (изврат из-за тупой реализации черепицы в PHP)
        $p8 = self::CreateAlphaPng($width - 8, 6);

        imagesettile($p8, $parts[8]);
        imagefilledrectangle($p8, 0, 0, $width - 9, 5, IMG_COLOR_TILED);
        imagecopy($result, $p8, 4, $height - 6, 0, 0, $width - 8, 6);

        // Сохраняем прозрачность
        imagesavealpha($result, true);
        return $result;
    }

    /**
     * @return int Кол-во иконок
     */
    final protected function GetIconsCount()
    {
        return count($this->IconsList);
    }

    final protected function SetLabel($map, $label, $labelColor1, $labelColor2, $icon_x, $icon_y, $icon_w, $icon_h)
    {
        // Делим надпись на две части
        $parts = explode('_', $label, 2);

        // Рассчитываем координаты вставки
        $label1_x = $icon_x + $icon_w + $this->VerticalSpace * 2;
        $label1_y = $icon_y + $icon_h / 2 + $this->FontSize / 1.8;

        // Выводим левую часть
        $label1Coordinates = imagefttext($map, $this->FontSize, 0 /*Угол поворота*/,
            $label1_x, $label1_y,
            $labelColor1,
            $this->Font,
            $parts[0]);

        // Если есть правая - выводим и её
        if (count($parts) > 1) {
            $label2_x = $label1Coordinates[4]; // X верхнего правого угла первой части надписи
            $label2_y = $label1_y;

            imagefttext($map, $this->FontSize, 0,
                $label2_x, $label2_y,
                $labelColor2,
                $this->Font,
                    '_' . $parts[1]);
        }
    }

    final protected function SetHeader($map, $labelColor)
    {
        $header_x = $this->BorderWidth;
        $header_y = $this->BorderWidth + $this->HeaderFontSize;

        imagefttext($map, $this->HeaderFontSize, 0,
            $header_x, $header_y,
            $labelColor,
            $this->Font,
            $this->HeaderText);
    }

    final protected function SetCopyrightAndLogo($map, $labelColor)
    {
        $text = sprintf("Version: %s\nRelease date: %s\n%s",
            $this->Version,
            $this->VersionDate,
            $this->CopyrightText);

        // Добавляем логотип
        $copy_width = self::GetLabelWidth($text,
            $this->Font,
            $this->CopyrightFontSize);

        // Рассчитываем координаты вставки логотипа
        $logo_x = $this->MapWidth
                - ($this->Logo['width']
                        + 2 * $this->HorisontalSpace
                        + $copy_width
                        + 4 * $this->HorisontalSpace
                        + $this->BorderWidth);

        $logo_y = $this->MapHeight - ($this->BorderWidth + $this->Logo['height']);

        imagecopy($map, $this->Logo['img'],
            $logo_x, $logo_y, // Координаты куда вставлять
            0, 0, // Координаты начала вставляемой области внутри логотипа
            $this->Logo['width'], $this->Logo['height']); // Координаты конца вставляемой области внутри логотипа

        // Добавляем надпись с версией и копирайтом
        $copy_x = $logo_x + $this->Logo['width'] + 2 * $this->HorisontalSpace;

        $copy_y = $logo_y
                + $this->Logo['height'] / 2
                - $this->CopyrightFontSize * 3 / 2;

        imagefttext($map, $this->CopyrightFontSize, 0,
            $copy_x, $copy_y,
            $labelColor,
            $this->Font,
            $text);
    }

    final protected function GetLogo()
    {
        $this->Logo = array();
        $img = imagecreatefrompng($this->LogoFile);
        $this->Logo['img'] = $img;
        $this->Logo['width'] = imagesx($img);
        $this->Logo['height'] = imagesy($img);
    }

    final protected static function CreateAlphaPng($width, $height)
    {
        $result = imagecreatetruecolor($width, $height);
        $transparent = imagecolorexactalpha($result, 255, 255, 255, 127);
        imagefill($result, 1, 1, $transparent);
        return $result;
    }

    final protected static function GetLabelWidth($text, $font, $fontSize)
    {
        $textBox = imagettfbbox($fontSize, 0, $font, $text);
        return $textBox[4];
    }

    final protected static function GetLimitedLabel($text, $font, $fontSize, $limitWidth)
    {
        if (self::GetLabelWidth($text, $font, $fontSize) > $limitWidth) {
            while (self::GetLabelWidth($text . '…', $font, $fontSize) > $limitWidth)
            {
                $text = substr($text, 0, strlen($text) - 1);
            }
            $text .= '…';
        }
        return $text;
    }

    final protected function SetFrameForLabel($map, $label, $icon_x, $icon_y, $icon_w, $icon_h, $mode)
    {
        // Расчет ширины метки
        $labelWidth = self::GetLabelWidth($label, $this->Font, $this->FontSize) - $this->VerticalSpace * 2;

        // Создаем рамку по высоте в 2 раза больше размера шрифта
        $frame_width = $labelWidth + $this->VerticalSpace * 6;
        $frame_height = $this->FontSize * 2;
        $frame = self::GenerateFrame($frame_width, $frame_height, $mode);

        // Рассчет координат места вставки рамки
        $frame_x = $icon_x + $icon_w + $this->VerticalSpace;
        $frame_y = $icon_y
                + $icon_h / 2
                - $frame_height / 2;

        // Добавляем рамку к основному изображению
        imagecopy($map, $frame,
            $frame_x, $frame_y,
            0, 0,
            $frame_width, $frame_height);

        // Подчищаем память
        imagedestroy($frame);
    }
}